﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace OpSatPlan
{
    public class Operator
    {
        private ActionDefinition mDefinition;
        private List<Action> mActions;
        private List<List<AtomicFormula>> mPreconditions;
        private List<List<AtomicFormula>> mAddEffects;
        private List<List<AtomicFormula>> mDeleteEffects;
		private int? mHashCode;
		private Dictionary<AtomicFormula, AtomicFormula> mPreconditionMap;
		private Dictionary<AtomicFormula, AtomicFormula> mAddEffectMap;
		private Dictionary<AtomicFormula, AtomicFormula> mDeleteEffectMap;
        public Operator(ActionDefinition definition)
        {
            this.mDefinition = definition;
            this.mActions = new List<Action>();
            this.mPreconditions = new List<List<AtomicFormula>>();
            this.mAddEffects = new List<List<AtomicFormula>>();
            this.mDeleteEffects = new List<List<AtomicFormula>>();
			this.mPreconditionMap = new Dictionary<AtomicFormula, AtomicFormula>();
			this.mAddEffectMap = new Dictionary<AtomicFormula, AtomicFormula>();
			this.mDeleteEffectMap = new Dictionary<AtomicFormula, AtomicFormula>();
        }
        public void Add(Action a)
        {
            if (!this.mActions.Contains(a))
            {
                this.mActions.Add(a);
                for (int i = 0; i < a.Preconditions.Count; i++)
                {
                    while (i >= this.mPreconditions.Count)
                        this.mPreconditions.Add(new List<AtomicFormula>());
                    if(!this.mPreconditions[i].Contains(a.Preconditions[i]))
                        this.mPreconditions[i].Add(a.Preconditions[i]);
					if(!this.mPreconditionMap.ContainsKey(a.Preconditions[i]))
						this.mPreconditionMap.Add(a.Preconditions[i], a.Preconditions[i]);
                }
                for (int i = 0; i < a.AddEffects.Count; i++)
                {
                    while (i >= this.mAddEffects.Count)
                        this.mAddEffects.Add(new List<AtomicFormula>());
                    if (!this.mAddEffects[i].Contains(a.AddEffects[i]))
                        this.mAddEffects[i].Add(a.AddEffects[i]);
					if(!this.mAddEffectMap.ContainsKey(a.AddEffects[i]))
						this.mAddEffectMap.Add(a.AddEffects[i], a.AddEffects[i]);
                }
                for (int i = 0; i < a.DeleteEffects.Count; i++)
                {
                    while (i >= this.mDeleteEffects.Count)
                        this.mDeleteEffects.Add(new List<AtomicFormula>());
                    if (!this.mDeleteEffects[i].Contains(a.DeleteEffects[i]))
                        this.mDeleteEffects[i].Add(a.DeleteEffects[i]);
					if(!this.mDeleteEffectMap.ContainsKey(a.DeleteEffects[i]))
						this.mDeleteEffectMap.Add(a.DeleteEffects[i], a.DeleteEffects[i]);
                }
            }
        }
        public ReadOnlyCollection<ReadOnlyCollection<AtomicFormula>> Preconditions
        {
            get
            {
                List<ReadOnlyCollection<AtomicFormula>> list = new List<ReadOnlyCollection<AtomicFormula>>();
                foreach (List<AtomicFormula> forms in this.mPreconditions)
                {
                    list.Add(new ReadOnlyCollection<AtomicFormula>(forms));
                }
                return new ReadOnlyCollection<ReadOnlyCollection<AtomicFormula>>(list);
            }
        }

        public ReadOnlyCollection<ReadOnlyCollection<AtomicFormula>> AddEffects
        {
            get
            {
                List<ReadOnlyCollection<AtomicFormula>> list = new List<ReadOnlyCollection<AtomicFormula>>();
                foreach (List<AtomicFormula> forms in this.mAddEffects)
                {
                    list.Add(new ReadOnlyCollection<AtomicFormula>(forms));
                }
                return new ReadOnlyCollection<ReadOnlyCollection<AtomicFormula>>(list);
            }
        }

        public ReadOnlyCollection<ReadOnlyCollection<AtomicFormula>> DeleteEffects
        {
            get
            {
                List<ReadOnlyCollection<AtomicFormula>> list = new List<ReadOnlyCollection<AtomicFormula>>();
                foreach (List<AtomicFormula> forms in this.mDeleteEffects)
                {
                    list.Add(new ReadOnlyCollection<AtomicFormula>(forms));
                }
                return new ReadOnlyCollection<ReadOnlyCollection<AtomicFormula>>(list);
            }
        }
        public ReadOnlyCollection<Action> Actions
        {
            get
            {
                return new ReadOnlyCollection<Action>(this.mActions);
            }
        }
        public override string ToString()
        {
            return "(" + this.mDefinition.Name + ")";
        }
		
		public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            else if (obj.GetType() == typeof(Operator))
                return this == (Operator)obj;
            else
                return false;
        }

        public static bool operator ==(Operator x, Operator y)
        {
            if (System.Object.ReferenceEquals(x, y))
                return true;
            else if ((object)x == null || (object)y == null)
                return false;
            else
                return x.GetHashCode() == y.GetHashCode();

        }

        public static bool operator !=(Operator x, Operator y)
        {
            return !(x == y);
        }

        public override int GetHashCode()
        {
            if (this.mHashCode == null)
            {
                int result = 17;
                result = result * 37 + this.mDefinition.GetHashCode();
                this.mHashCode = result;
            }
            return this.mHashCode.Value;
        }
		
		public bool IsMutex(Operator op)
		{
			bool mutex = false;
			if(op == this)
				return false;
			IEnumerable<List<AtomicFormula>> pre;
			IEnumerable<List<AtomicFormula>> del;
			IEnumerable<List<AtomicFormula>> add;
			
			IEnumerable<List<AtomicFormula>> oPre;
			IEnumerable<List<AtomicFormula>> oDel;
			IEnumerable<List<AtomicFormula>> oAdd;
			
			
			del = from x in this.mDeleteEffects
					where x.Count == 1
					select x;
			
			if(del.Count() != 0)
			{
				oPre = from x in op.mPreconditions
					where x.Count == 1
					select x;
				oAdd = from x in op.mAddEffects
					where x.Count == 1
					select x;
				if(oPre.Count() != 0 || oAdd.Count() != 0)
				{
					foreach(List<AtomicFormula> afs in del)
					{
						foreach(List<AtomicFormula> oAfs in oPre)
						{
							if(oAfs[0] == afs[0])
							{
								mutex = true;
								break;
							}
						}
						if(!mutex)
						{
							foreach(List<AtomicFormula> oAfs in oAdd)
							{
								if(oAfs[0] == afs[0])
								{
									mutex = true;
									break;
								}
							}	
						}
						if(mutex)
							break;
					}
				}
			}
			
			oDel = from x in op.mDeleteEffects
					where x.Count == 1
					select x;
			if(oDel.Count() != 0 && !mutex)
			{
				pre = from x in this.mPreconditions
						where x.Count == 1
						select x;
				add = from x in this.mAddEffects
						where x.Count == 1
						select x;
				if(pre.Count() != 0 || add.Count() != 0)
				{
					foreach(List<AtomicFormula> afs in oDel)
					{
						foreach(List<AtomicFormula> oAfs in pre)
						{
							if(oAfs[0] == afs[0])
							{
								mutex = true;
								break;
							}
						}
						if(!mutex)
						{
							foreach(List<AtomicFormula> oAfs in add)
							{
								if(oAfs[0] == afs[0])
								{
									mutex = true;
									break;
								}
							}	
						}
						if(mutex)
							break;
					}
				}
			}
			
			return mutex;
		}
		
		public bool MayBeMutex(Operator op)
		{
			bool res = this.mDeleteEffectMap.Keys.Any(k => op.mPreconditionMap.ContainsKey(k) ||
															op.mAddEffectMap.ContainsKey(k));
			if(!res)
			{
				res = op.mDeleteEffectMap.Keys.Any(k => this.mPreconditionMap.ContainsKey(k) ||
														this.mAddEffectMap.ContainsKey(k));
			}
			return res;
		}
    }
}
